package com.rpc.core;

import com.rpc.core.annotation.RpcExport;
import com.rpc.core.annotation.Version;
import com.rpc.core.param.RpcParams;
import com.rpc.core.filter.InvokerChain;
import com.rpc.core.filter.RpcServiceFilter;
import com.rpc.core.holder.bean.Bean;
import com.rpc.core.holder.bean.ExportBean;
import com.rpc.core.holder.rag.RegInfo;
import com.rpc.core.holder.transmit.Request;
import com.rpc.core.holder.transmit.Response;
import com.rpc.core.register.RpcRegister;
import com.rpc.core.remoting.impl.NettyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;


public class RpcService implements InitializingBean, DisposableBean, ApplicationContextAware {


    private Logger logger = LoggerFactory.getLogger(RpcService.class);

    private AtomicBoolean isStart = new AtomicBoolean(false);

    private ApplicationContext context;

    private ConcurrentHashMap<String, Bean> exportMap = new ConcurrentHashMap<>();

    private RpcRegister register;

    private NettyService netty;

    private InvokerChain invokerChain;

    //参数列表
    RpcParams params;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
        this.params = this.context.getBean(RpcParams.class);
    }

    public ApplicationContext getContext() {
        return context;
    }

    @Override
    public void destroy() throws Exception {
        this.register.close();
        this.netty.stop();
    }

    @Override
    public void afterPropertiesSet() throws Exception {


        if (!isStart.getAndSet(true)) {
            logger.info("rpc server start ...");
            //1.找到要暴露的
            buildExport();
            //2. 启动本地服务器
            localServerStart();
            //3.建立和注册中心的联系
            buildConnectionToRegister();

        }

    }

    private void localServerStart() {
        netty = new NettyService();
        netty.setService(this);
        netty.start();
    }

    private void buildConnectionToRegister() {

        RegInfo regInfo = new RegInfo();
        regInfo.setAddress(params.getHost() + ":" + params.getPort());
        regInfo.setAppName(params.getAppName());

        Object bean = context.getBean(params.getRegisterType());

        if (!(bean instanceof RpcRegister)) {
            throw new RuntimeException("The registrar could not be found!");
        }

        this.register = (RpcRegister) bean;
        List<String> exports = new ArrayList<>(this.exportMap.keySet());
        regInfo.setExportList(exports);
        this.register.registered(regInfo);

    }

    private void buildExport() {
        Map<String, Object> beans = context.getBeansWithAnnotation(RpcExport.class);

        InvokerChain baseChain = buildInvokerChain();

        if (beans != null && beans.size() > 0) {
            for (Object value : beans.values()) {
                RpcExport annotation = value.getClass().getAnnotation(RpcExport.class);

                //全局版本号
                String globalVersion = annotation.version();
                //每个接口对应的version
                Version[] versions = annotation.versions();

                Class<?>[] interfaces = value.getClass().getInterfaces();

                Arrays.stream(interfaces).map(iface -> {
                    for (Version version : versions) {
                        if (version.interfaceClass().equals(iface)) {
                            return iface.getName() + ":" + version.version();
                        }
                    }
                    return iface.getName() + ":" + globalVersion;
                }).forEach(itag -> {
                    Bean bean = new ExportBean(value, itag);
                    exportMap.put(itag, bean);
                });

            }
            baseChain.setExportMap(this.exportMap);
        }

        invokerChain = baseChain;
    }

    private InvokerChain buildInvokerChain() {
        Map<String, RpcServiceFilter> filterMap = context.getBeansOfType(RpcServiceFilter.class);
        List<RpcServiceFilter> lists;
        if (filterMap == null) {
            lists = new ArrayList<>();
        } else {
            lists = new ArrayList<>(filterMap.values());
        }

        return new InvokerChain(lists);
    }

    public Response invoke(Request request) {
        return invokerChain.invoke(request);
    }
}
